/**************************************************************************************************
 * light library                                                                                  *
 * Copyright © 2012 David Kretzmer                                                                *
 *                                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software  *
 * and associated documentation files (the "Software"), to deal in the Software without           *
 * restriction,including without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute,sublicense, and/or sell copies of the Software, and to permit persons to whom the   *
 * Software is furnished to do so, subject to the following conditions:                           *
 *                                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies or       *
 * substantial portions of the Software.                                                          *
 *                                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING  *
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND     *
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.        *
 *                                                                                                *
 *************************************************************************************************/

#pragma once

#include <light/light.hpp>


namespace light
{
namespace utility
{
	//=================================================================================================
	//
	//=================================================================================================
	template<typename Type>
	void typeless_default_deleter(void *ptr, size_t type_hash)
	{
		assert(typeid(Type).hash_code() == type_hash);
		delete static_cast<Type*>(ptr);
	}

	class typeless_ptr : light::non_copyable
	{
	public:
		typedef void (*deleter_type)(void *ptr, size_t type_hash);

		// Constructor
		template<typename PointerType>
		explicit typeless_ptr(PointerType *ptr, deleter_type deleter = typeless_default_deleter<PointerType>) :
			m_pointer(ptr),
			m_type_hash(typeid(PointerType).hash_code()),
			m_deleter(deleter)
		{

		}

		// Move constructor
		typeless_ptr(typeless_ptr &&rhs) :
			m_pointer(rhs.m_pointer),
			m_type_hash(rhs.m_type_hash),
			m_deleter(rhs.m_deleter)
		{
			rhs.m_pointer = nullptr;
			rhs.m_type_hash = 0;
			rhs.m_deleter = nullptr;
		}

		// Destructor
		~typeless_ptr()
		{
			if(m_pointer)
				m_deleter(m_pointer, m_type_hash);
		}

		// Move assignment
		typeless_ptr& operator = (typeless_ptr &&rhs)
		{
			m_pointer = rhs.m_pointer;
			m_type_hash = rhs.m_type_hash;
			m_deleter = rhs.m_deleter;

			rhs.m_pointer = nullptr;
			rhs.m_type_hash = 0;
			rhs.m_deleter = nullptr;

			return *this;
		}

		// Access
		template<typename Type>
		Type* get_as() const
		{
			assert(typeid(Type).hash_code() == m_type_hash);
			return static_cast<Type*>(m_pointer);
		}

		void* get() const { return m_pointer; }

	private:
		void *m_pointer;
		size_t m_type_hash;
		deleter_type m_deleter;
	};

} // namespace: utility

using utility::typeless_ptr;

} // namespace: light
